﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace HLSL
{   
    public class CModel
    {
        #region Field
        public Vector3 Positon { get; set; } //vị trí của vât
        public Vector3 Rotation { get; set; }//Quay vật
        public Vector3 Scale { get; set; }//Kéo dãn vật
        public Model Model { get; private set; }//Vật thể không gian
        public Material Material { get; set; }//vật chất ánh sáng

        private Matrix[] modelTranforms;//ma trận dùng để lưu cấu tạo vật thể
        private GraphicsDevice graphicDevice; //GraphicDevice
        private BoundingSphere boudingSphere; //
        #endregion

        /// <summary>
        /// Hàm khởi tạo CModel
        /// </summary>
        /// <param name="Model">Vật thể</param>
        /// <param name="Position">Vị trí</param>
        /// <param name="Rotation">Quay vật</param>
        /// <param name="Scale">Kéo dãn</param>
        /// <param name="graphicDevice">GraphicDevice</param>
        public CModel(Model Model, Vector3 Position, Vector3 Rotation, Vector3 Scale, GraphicsDevice graphicDevice)
        {
            this.Model = Model;
            this.Positon = Position;
            this.Rotation = Rotation;
            this.Scale = Scale;            
            this.graphicDevice = graphicDevice;
            this.Material = new Material();
            modelTranforms= new Matrix[Model.Bones.Count];//khởi tạo ma trận bằng với số xương của Model
            Model.CopyAbsoluteBoneTransformsTo(modelTranforms);//Gán cho ma trận giá trị tạo nên Model
            builtBoudingSphere();
            generateTag();

        }
        /// <summary>
        /// Ý tưởng xét va chạm
        /// Load 1 Models nhỏ hơn vào đầu của con tàu và không Draw nó, để chế độ ẩn.
        /// Nếu va chạm thì con tàu sẽ đứng yên, không di chuyển nữa. Khi đó nó sẽ xoay 1 góc nào đó.
        /// Ok khi xoay thì model ảo sẽ ra ngoài vùng va chạm và con tàu tiếp tục di chuyển.
        /// </summary>
        private void builtBoudingSphere()
        {
            BoundingSphere sphere = new BoundingSphere(Vector3.Zero, 0);
            foreach (ModelMesh mesh in Model.Meshes)
            {              
                BoundingSphere tranformed = mesh.BoundingSphere.Transform(modelTranforms[mesh.ParentBone.Index]);
                sphere = BoundingSphere.CreateMerged(sphere, tranformed);//tao thanh 1 khoi

            }
            this.boudingSphere = sphere;
        }
        
        public bool CheckColision(CModel otherModel)
        {
              
            return (this.BoudingSphere.Contains(otherModel.BoudingSphere)==ContainmentType.Intersects);
        }
        public BoundingSphere BoudingSphere
        {
            get 
            {
                Matrix worldTranform = Matrix.CreateScale(Scale) * Matrix.CreateTranslation(Positon);
                BoundingSphere transformed = boudingSphere;
                transformed = transformed.Transform(worldTranform);
                return transformed;
            }
        }
        /// <summary>
        /// Đây là hàm Draw 1 Model
        /// </summary>
        /// <param name="View">Ma trận View</param>
        /// <param name="Projection">Ma trận Projection</param>
        public void Draw(Matrix View,Matrix Projection, Vector3 CameraPosition)
        {
            //Đầu tiên khởi tạo 1 ma trận baseWorld
            //No là ma trận nhân của 3 ma trận: ma trận Kéo dãn, mt xoay, mt vị trí.
            Matrix baseWorld = Matrix.CreateScale(Scale) * Matrix.CreateFromYawPitchRoll(Rotation.Y,Rotation.X,Rotation.Z) * Matrix.CreateTranslation(Positon);
            //Duyệt từng Mesh trong Model đó
            foreach (ModelMesh mesh in Model.Meshes)
            {
                //Ma trận localWorld là 1 ma trận trong mảng ma trận modelTranforms với 1 chỉ số ParentBone tương ứng nhận với baseWorld
                Matrix localWorld= modelTranforms[mesh.ParentBone.Index]*baseWorld;
                //Duyệt từng phần trong Mesh
                foreach (ModelMeshPart partMesh in mesh.MeshParts)
                {
                    Effect effect= partMesh.Effect;//Lấy ra effect
                    if (effect is BasicEffect)
                    {
                        ((BasicEffect)effect).EnableDefaultLighting();//Chiếu sáng mặc định
                        ((BasicEffect)effect).World = localWorld;//Tạo world
                        ((BasicEffect)effect).Projection = Projection;//Projection
                        ((BasicEffect)effect).View = View;//Lookat
                    }
                    else
                    {
                        Material.SetEffectParamaters(effect);
                        setEffectParamater(effect, "World", localWorld);
                        setEffectParamater(effect,"Projection",Projection);
                        setEffectParamater(effect,"View",View);
                        setEffectParamater(effect, "CameraPosition", CameraPosition);
                        
                    }

                   
                }
                mesh.Draw();//Vẽ mesh
            }
            
        }
        /// <summary>
        /// Tạo ra Mesh Tag
        /// gán cho Tag của mỗi ModelMeshPart 
        /// </summary>
        public void generateTag()
        {
            foreach (ModelMesh mesh in Model.Meshes)
            {
                foreach (ModelMeshPart part in mesh.MeshParts)
                {
                    if (part.Effect is BasicEffect)
                    {
                        BasicEffect effect = (BasicEffect)part.Effect;
                        MeshTag tag = new MeshTag(effect.DiffuseColor,effect.Texture,effect.SpecularPower);
                        part.Tag = tag;//part.Tag la dang object
                    }
                }
            }
        }
        /// <summary>
        /// Lưu Effect hiện tại lại 
        /// </summary>
        public void CacheEffect()
        {
            foreach (ModelMesh mesh in Model.Meshes)
            {
                foreach (ModelMeshPart part in mesh.MeshParts)
                {
                    ((MeshTag)part.Tag).CachedEffect = part.Effect;
                }
            }
        }
        /// <summary>
        /// Sử dụng lại effect đã lưu trữ
        /// part.Effect= Part.Tag.CachedEffect
        /// </summary>
        public void RestoreEffect()
        {
            foreach (ModelMesh mesh in Model.Meshes)
            {
                foreach (ModelMeshPart part in mesh.MeshParts)
                {
                    if (((MeshTag)part.Tag).CachedEffect!=null)
                    {
                        part.Effect = ((MeshTag)part.Tag).CachedEffect;
                    }
                }
            }
        }
        /// <summary>
        /// Set các thành phần Effect "paramaterName" đưa vào 1 giá trị
        /// </summary>
        /// <param name="effect">Effect đưa vào</param>
        /// <param name="paramaterName">Thành phần effect đó</param>
        /// <param name="val">Giá trị của nó</param>
        public void setEffectParamater(Effect effect, string paramaterName, object val)
        {
            if (effect.Parameters[paramaterName]==null)
                return;
            if (val is Vector3)
                effect.Parameters[paramaterName].SetValue((Vector3)val);
            if (val is bool)
                effect.Parameters[paramaterName].SetValue((bool)val);
            if (val is Matrix)
                effect.Parameters[paramaterName].SetValue((Matrix)val);
            if (val is Texture2D)
                effect.Parameters[paramaterName].SetValue((Texture2D)val);
        }
        //Set model mesh part Effect
        public void SetModelEffect(Effect effect, bool CopyEffect)
        {
            //duyệt từng meshpart
            foreach (ModelMesh mesh in Model.Meshes)
            {
                foreach (ModelMeshPart part in mesh.MeshParts)
                {

                    Effect toSet = effect;
                    if (CopyEffect)
                    {
                        toSet = effect.Clone();
                    }
                    MeshTag tag = (MeshTag)part.Tag;//??? value use to set
                    if (tag.Texture != null)
                    {
                        setEffectParamater(toSet, "BasicTexture", tag.Texture);
                        setEffectParamater(toSet, "TextureEnabled", true);

                    }
                    else
                        setEffectParamater(toSet, "TextureEnable", false);
                    setEffectParamater(toSet, "DiffuseColor",tag.Color);
                    setEffectParamater(toSet, "SpecularPower", tag.SpecularPower);
                    part.Effect = toSet;

                }
            }
        }
    }
}
